﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using BomberManServer.Utils;
using BomberManServer.Gaming.Object;
using BomberManServer.Gaming.Object.Stats;
using BomberManServer.Gaming.Object.Mapping.Items;
using BomberManServer.Gaming.Object.Mapping;
using BomberManServer.Gaming.Socket;
using BomberManServer.Gaming.Time;

namespace BomberManServer.Gaming
{
    public class RuleKeeper : InGame
    {
        private UInt32 nbRound;
        
        public List<PlayerState> players;
        private Logger.Handler log;
        private Trigger timerRound;
        private Trigger timerScoreBoard;
        private Trigger timerEndRound;

        public RuleKeeper(Game game) : base(game)
        {
            this.players = new List<PlayerState>();
            foreach(Player player in game.match.players)
                this.players.Add(new PlayerState(player));
            this.nbRound = 0;
            log = game.logger.createHandler("Rules");
            timerRound = new Trigger(Constants.instance.timerRound * Constants.instance.actions_per_second, endRound);
            timerScoreBoard = new Trigger(Constants.instance.timerScoreBoard * Constants.instance.actions_per_second, nextAction);
            timerEndRound = new Trigger(Constants.instance.actions_per_second, endRound);
        }

        public void nextAction()
        {
            log.information("Next Action : " + DateTime.Now.Hour + " :" + DateTime.Now.Minute + " :" + DateTime.Now.Second);
            game.scheduler.remove(timerScoreBoard);
            game.scheduler.schedule(timerRound);

            nbRound++;
            log.information("New Round");
            game.map.reset();
            timerRound.reset();

        }

        public bool roundable()
        {
            switch(game.match.type)
            {
                case Match.MatchType.FFA :
                    return !checkPlayerWin();
                case Match.MatchType.SOLO :
                    return !checkTeamWin();
                case Match.MatchType.TEAM :
                    return !checkTeamWin();
            }
            log.information("Match Type invalid");
            return false;
        }

        public bool checkTeamWin()
        {
            for (int i = 0; i < game.stats.teamStats.Count(); i++)
            {
                if (game.stats.teamStats[i].RoundWin == Constants.instance.requiredTeamWin)
                    return true;
            }
            return false;
        }

        public bool checkPlayerWin()
        {
            foreach (TeamStats teams in game.stats.teamStats)
                foreach (PlayerStats player in teams.players)
                    if ((player.getScore() == Constants.instance.requiredFFAKill) || (this.nbRound == Constants.instance.maxFFARound))
                        return true;
            return false;
        }

        public void endGame()
        {
            log.information("End of the game");

            game.sender.onEndGame(this.game.match);

            game.finished = true;
        }

        public void endRound()
        {
            log.information("enter method : " + DateTime.Now.Hour + " :" + DateTime.Now.Minute + " :" + DateTime.Now.Second);
            game.scheduler.stop();
            game.scheduler.remove(timerEndRound);
            log.information("End of the round");
            if (roundable())
                roundScoreboard();
            else
                endGame();
            
        }

        public void roundScoreboard()
        {
            log.information("Scoreboard");
            // envoi des données au client pour e display
            game.scheduler.schedule(timerScoreBoard);
            game.sender.onScoreBoard(game.match);
            
            //else
            //    timerScoreBoard.reset();
        }


        public void avatarDie(Avatar killer, Avatar killed)
        {
            killed.die();
            game.sender.onPlayerKilled(killed.player);
            if (killer.player.id == killed.player.id)
            {
                log.information("Avatar " + killed.id +" commited suicide");
                game.stats.getPlayer(killed.player.uid).addSuicide();
            }
            else
            {
                log.information("Avatar " + killed.id + "was killed by Avatar " + killer.id);
                PlayerStats killerPlayer = game.stats.getPlayer(killer.player.uid);
                game.stats.getPlayer(killed.player.uid).addDeath();
                killerPlayer.addKill();
            }

            switch (game.match.type)
            {
                case Match.MatchType.FFA:
                    if (playerHasWin(killer.player))
                    {
                        
                        game.scheduler.schedule(timerEndRound);
                    }
                    break;
                case Match.MatchType.SOLO:
                    if (teamHasWin())
                    {
                        game.stats.teamStats[killer.player.id].RoundWin++;
                        log.information("Round End : " + DateTime.Now.Hour + " :" + DateTime.Now.Minute + " :" + DateTime.Now.Second);
                        game.scheduler.schedule(timerEndRound);
                    }
                    break;
                case Match.MatchType.TEAM:
                    if (teamHasWin())
                    {
                        game.stats.teamStats[killer.player.id].RoundWin ++;
                        game.scheduler.schedule(timerEndRound);
                    }
                    break;
            }
        }

        public bool playerHasWin(Player player)
        {

            if (game.stats.getPlayer(player.uid).getScore() == Constants.instance.requiredFFAKill)
            {
                return true;
            }

            if (this.game.match.getAlivePlayers().Count > 1)
                return false;
            else
            {
                this.game.stats.teamStats[this.game.stats.getPlayer(player.uid).teamID].winRound();
                return true;
            }
        }

        public bool teamHasWin()
        {
            bool hasNoAdversary = true;
            //check if all ennemi avatar are dead
            foreach (Player player in game.match.players)
            {
                foreach (Player adversaries in game.match.getPlayerNotFromTeamID(game.stats.getPlayer(player.uid).teamID))
                {
                    //if (adversaries.uid == player.uid)
                    //    continue;
                    if ((adversaries.avatar.isAlive)&&(player.avatar.isAlive))
                        hasNoAdversary = false;
                }
                if ((hasNoAdversary)&&(player.avatar.isAlive))
                {
                    this.game.stats.teamStats[this.game.stats.getPlayer(player.uid).teamID].winRound();
                    return hasNoAdversary;
                }
            }
            return hasNoAdversary;
        }

        // Return true if every player are ready.
        public void playerIsReady(Player player)
        {
            (players[(int)player.id]).state = ConnectionWatcher.ConnectionState.READY;
            foreach (PlayerState pState in players)
            {
                    if (pState.state != ConnectionWatcher.ConnectionState.READY)
                        return;
            }
            nextAction();
        }

    }
}
